<?php

define( "PRINCIPAL_OK",                 0  );
define( "PRINCIPAL_CRED_INSUFFICIENT",  1  );
define( "PRINCIPAL_USER_UNKNOWN",       2  );
define( "PRINCIPAL_AUTH_ERR",           4  );
define( "PRINCIPAL_NEW_AUTHTOK_REQD",   5  );
define( "PRINCIPAL_ERR",                255 );

/*
 * Класс аутентификации. Умеет перебирать методы аутентификации, до тех пор пока по какому-нибудь методу не 
 * выполнится успешная аутентификация. Для каждого метода перебираются все "входы" (экземпляры RL_Principal_Input)
 * 
 */
class RL_Principal
{
	public $methods = array(); // методы авторизации
	public $ask = array(); // входные параметры к методам
	private $storage_model;
	private $state_methods;
	private $session_methods;
	
	public function __construct( RL_SiteRequestHandler $rh, $storage_model="Store" )
	{
		$this->rh = $rh;
		$this->ctx = $rh->ctx;
		
		$this->rh->useClass("RL_Principal_Input_Cookie");
		$this->rh->useClass("RL_Principal_Input_Request");
		$this->rh->useClass("RL_Principal_Input_Array");
		
		$this->rh->useClass("RL_Principal_Auth_Common");
		$this->rh->useClass("RL_Principal_Auth_Digest");
		$this->rh->useClass("RL_Principal_Auth_LoginPassword");
		$this->rh->useClass("RL_Principal_Auth_Openid");
		
		//. достроить стораж модель
		$this->rh->useClass("RL_Principal_Storage");
		$this->storage_model = RL_Principal_Storage::Factory( $this, $storage_model );
		$this->storage_model->magic_word = $this->rh->magic_word;
		
		$cookie_digest_params = array(
			'domain' => $this->rh->cookie_domain,
			'params' => array(
				'digest' => $this->rh->cookie_prefix.'_principal_digest',
			),
		);
		$cookie_digest = new RL_Principal_Input_Cookie($cookie_digest_params);

		$req_login_pw_params = array(
			'method' => 'get',
			'params' => array(
				'login'    => '_principal_login',
				'password' => '_principal_password',
			),
		);
		$req_login_pw = new RL_Principal_Input_Request($req_login_pw_params);

		$req_openid_params = array(
			'method' => 'request', 
			'params' => array(
				'openid_action' => 'openid_action',
				'openid_url'    => 'openid_url',
			),
		);
		
		$req_openid = new RL_Principal_Input_Request($req_openid_params);
		
		// у нас есть еще возможность посылать через протокол openid вместо identity логин и пароль
		$req_openid_pl = new RL_Principal_Input_Request(
			array(
				'method' => 'request', 
				'params' => array(
					'openid_action' => 'openid_action',
					'openid_login' => 'openid_login',
					'openid_password'    => 'openid_password',
				),
			)
		);
		
		//$ask_test = new RL_Principal_Input_Array(array('login'=>'funca.myopenid.com', 'password'=>'123'));
		$this->ask['digest'][] = $cookie_digest;
		$this->ask['login'][] = $req_login_pw;

		$this->ask['openid'][] = $req_openid;
		$this->ask['openid'][] = $req_openid_pl;
		$this->ask['openid'][] = TRUE; // это делается для повторного возврата в RL_Principal_Auth_Openid::identify() после редиректа от идентити-провайдера
		
		$this->methods[ 'digest'] = new RL_Principal_Auth_Digest(array(
			'store' => $this->storage_model,
			'cookie_name' => $this->rh->cookie_prefix.'_principal_digest',
			'cookie_domain' => $this->rh->cookie_domain,
			'cookie_expire_days' => $this->rh->cookie_expire_days,
		));
		$this->methods[ 'login'] = new RL_Principal_Auth_LoginPassword(array('store' => $this->storage_model));
		$this->methods['openid'] = new RL_Principal_Auth_Openid(array(
			//'login_url' => $this->rh->base_url.'z/login',
			//'login_url' => $this->rh->SERVER['REQUEST_URI'],
			'trust_url'  => $this->rh->base_url,
			'openid_srv_url' => $this->rh->idsrv_openidsrv_url,
		));
		$this->state_methods = array(); // методы аутентифкации у которых меняются состояния
		$this->state_methods['digest'] = $this->methods['digest'];
		
		$this->session_methods = array(); // методы имеющие дело с сессией
		$this->session_methods['digest'] = $this->methods['digest'];
	} // end of __construct
	
	public function identify ($input=NULL, $facility=NULL)
	{
		$this->method = NULL;
		$this->cred = NULL;
		$this->data = NULL;
		$methods = isset($facility) ? array($facility => $this->methods[$facility]) : $this->methods;
		$inputs  = isset($input)    ? array($facility => array($input)) : $this->ask;

		foreach ($methods as $facility => $method) {
			foreach ($inputs[$facility] as $input) {
				if ($input === TRUE || $input->ask()) {
					if ($this->cred = $method->identify($input)) {
						if ($input !== TRUE) $input->success();
						$this->method = $method;
						break 2;
					} else {
						if ($input !== TRUE) $input->error();
						$this->error = $method->getErrorRussianDescr();
						break 2;
					}
				}
			}
		}
		if ($this->method) {
			$this->data = $this->method->getProfile($this->cred);
			return PRINCIPAL_OK;
		}
		return PRINCIPAL_USER_UNKNOWN;
	} // end of function identify

	public function login ($input=NULL, $facility=NULL)
	{
		if (PRINCIPAL_OK === ($res = $this->identify($input, $facility))) {
			$this->setOKState();
			foreach ($this->session_methods as $k=>$v) {
				$v->session_start();
			}
		}
		return $this->data;
	} // end of function login
	
	public function logout ()
	{
		foreach ($this->session_methods as $k=>$v) {
			$v->session_end();
		}
	} // end of function logout
	
	private function setOKState()
	{
		$cred = $this->data;

		foreach ($this->state_methods as $method) {
			$cred = $method->setOKState($cred);
		}
		$this->cred = $cred;
		return $this->cred;
	} // end of function setOKState()

	public function getProfile ()
	{
		return $this->data;
	} // end of function getProfile

	// --- old principal --- {{{
	public function guest ($profile = "guest" )
	{
		 // find script or return
		 $file_source = $this->rh->FindScript( "principal_profiles", $profile, false, -1 );
		 if ($file_source === false) return false;

		 // uplink
		 include( $file_source );
		 $this->data = $included_profile;
		 $this->data["guest_profile"] = $profile;
		 
		 return true;
	} // end of function guest
}
